#include "../os_mutex.h"
#include <stdlib.h>
#include <memory.h>

struct mk_mutex_t * mutex_create()
{
	struct mk_mutex_t * pMutex = NULL;
	pMutex = malloc(sizeof(struct mk_mutex_t));
	memset(pMutex, 0, sizeof(struct mk_mutex_t));
	return pMutex;

}

long mutex_init(struct mk_mutex_t * pMutex, const  char * szMutexName)
{
#ifdef _MSC_VER
	{
		BOOL bInitOk;
		BOOL bSetOk;
		DWORD dwErr;
		SECURITY_DESCRIPTOR SD;
		// declare and initialize a security attributes structure
		SECURITY_ATTRIBUTES MutexAttributes;

		if (!pMutex)
			return 0;

		if (pMutex->bInited)
		{
			return 0;
		}

		memset(&MutexAttributes, 0, sizeof(MutexAttributes));
		MutexAttributes.nLength = sizeof(MutexAttributes);
		MutexAttributes.bInheritHandle = FALSE; // object un-inheritable

		// declare and initialize a security descriptor
		bInitOk = InitializeSecurityDescriptor(&SD,
			SECURITY_DESCRIPTOR_REVISION);

		if (!bInitOk)
			return 1;

		// give the security descriptor a Null Dacl
		// done using the  "TRUE, (PACL)NULL" here
		bSetOk = SetSecurityDescriptorDacl(&SD,
			TRUE,
			(PACL)NULL,
			FALSE);

		if (!bSetOk)
			return 2;

		// Make the security attributes point
		// to the security descriptor
		MutexAttributes.lpSecurityDescriptor = &SD;

		// use the attributes with the Null Dacl to
		// create the mutex
		pMutex->hMutex = CreateMutex(&MutexAttributes,
			FALSE,
			szMutexName);

		if (pMutex->hMutex == NULL)
		{
			dwErr = GetLastError();
			pMutex->hMutex = CreateMutex(NULL, FALSE, szMutexName);

			if (pMutex->hMutex == NULL)
				return 3;
		}

		pMutex->bInited = TRUE;
		return 0;
	}
#else 
	pthread_mutex_init(&pMutex->mtx, NULL);
	return 0;
#endif

}

long mutex_open(struct mk_mutex_t * pMutex, char * szMutexName)
{
#ifdef _MSC_VER
	{
		BOOL bInitOk;
		BOOL bSetOk;
		SECURITY_DESCRIPTOR SD;
		// declare and initialize a security attributes structure
		SECURITY_ATTRIBUTES MutexAttributes;

		if (!pMutex)
			return 0;

		memset(&MutexAttributes, 0, sizeof(MutexAttributes));
		MutexAttributes.nLength = sizeof(MutexAttributes);
		MutexAttributes.bInheritHandle = FALSE; // object un-inheritable

		// declare and initialize a security descriptor
		bInitOk = InitializeSecurityDescriptor(&SD,
			SECURITY_DESCRIPTOR_REVISION);

		if (!bInitOk)
			return 1;

		// give the security descriptor a Null Dacl
		// done using the  "TRUE, (PACL)NULL" here
		bSetOk = SetSecurityDescriptorDacl(&SD,
			TRUE,
			(PACL)NULL,
			FALSE);

		if (!bSetOk)
			return 2;

		// Make the security attributes point
		// to the security descriptor
		MutexAttributes.lpSecurityDescriptor = &SD;

		// use the attributes with the Null Dacl to
		// create the mutex
		pMutex->hMutex = OpenMutex(READ_CONTROL, 1,
			szMutexName);

		if (pMutex->hMutex == NULL)
			return 3;

		pMutex->bInited = TRUE;
		pMutex->LockCount = 0;
		return 0;
	}


#else 
	return 0;
#endif

	
}

long mutex_lock(struct mk_mutex_t * pMutex, long timeOut)
{
#ifdef _MSC_VER
	{
		unsigned long dwWaitResult;
		int flag = 1;

		if(!pMutex)
			return 0;

		if (!pMutex->bInited)
			return 0;

		while (flag)
		{
			// Request ownership of mutex.
			dwWaitResult = WaitForSingleObject(
				pMutex->hMutex,   // handle to mutex
				timeOut);   // five-second time-out interval

			switch (dwWaitResult)
			{
				// The thread got mutex ownership.
			case WAIT_OBJECT_0:
				flag = 0;
				InterlockedIncrement(&pMutex->LockCount);
				break;

				// Cannot get mutex ownership due to time-out.
			case WAIT_TIMEOUT:
				return -1;

				// Got ownership of the abandoned mutex object.
				//The specified object is a mutex object that was not
				//released by the thread that owned the mutex object
				//before the owning thread terminated.
				//Ownership of the mutex object is granted to the calling thread,
				//and the mutex is set to non-signaled.

			case WAIT_ABANDONED: //also available
				ReleaseMutex(pMutex->hMutex);
				break;
			default:
				return -1;
			}
			Sleep(5);
		}

		return pMutex->LockCount;
	}
#else 
	pthread_mutex_lock(&pMutex->mtx);
	return 0;
#endif

}

long mutex_unlock(struct mk_mutex_t * pMutex)
{
#ifdef _MSC_VER
	if (pMutex != NULL && pMutex->hMutex && pMutex->LockCount > 0)
	{
		ReleaseMutex(pMutex->hMutex);
		InterlockedDecrement(&pMutex->LockCount);
		return pMutex->LockCount;
	}
	return 0;
#else 
	pthread_mutex_unlock(&pMutex->mtx);
	return 0;
#endif

}

long mutex_destroy(struct mk_mutex_t * pMutex)
{
	if (!pMutex)
	{
		return 0;
	}

#ifdef _MSC_VER
	while (pMutex->LockCount > 0)
	{
		mutex_unlock(pMutex);
	}

	if (pMutex->hMutex)
	{
		CloseHandle(pMutex->hMutex);
		pMutex->hMutex = NULL;
	}
#else 
	pthread_mutex_destroy(&pMutex->mtx);
#endif

	free(pMutex);
	return 0;
}
